//TODO: Gracefully refuse the client connection when Media Center is recording.
//TODO: Change this to be a windows forms app that sits in the system tray (or maybe a service once it becomes more reliable?)

using System;
using System.Collections;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Text;
using System.Threading;
using System.Net;
using System.Net.Sockets;

namespace TVServer
{
    /// <summary>
    /// This server receives network communications from a client and
    /// wraps VLC to initialize streaming.
    /// </summary>
    class TVServer
    {
        System.Net.Sockets.TcpListener server;
        static Process streamingProc;
        static string HELP = "help\nstart <channel#> [<videobitrate> <audiobitrate>]\nstop";

        /// <summary>
        /// Open a socket to listen for client connections. When a client disconnects,
        /// fire up a new socket to listen for another connection.
        /// Only one client is supported at a time.
        /// Ctrl-c is the only way to kill the server.
        /// </summary>
        public TVServer()
        {
            while (true)
            {
                int recv;
                byte[] data = new byte[1024];
                IPEndPoint ipep = new IPEndPoint(IPAddress.Any,
                                       Properties.Settings.Default.ServerPort);

                Socket newsock = new
                    Socket(AddressFamily.InterNetwork,
                                SocketType.Stream, ProtocolType.Tcp);

                newsock.Bind(ipep);
                newsock.Listen(10);
                Console.WriteLine("Waiting for a client...");
                Socket client = newsock.Accept();
                IPEndPoint clientep =
                             (IPEndPoint)client.RemoteEndPoint;
                Console.WriteLine("Connected with {0} at port {1}",
                                clientep.Address, clientep.Port);


                string welcome = "Connected to the TV Server";
                data = Encoding.ASCII.GetBytes(welcome);
                client.Send(data, data.Length, SocketFlags.None);

                bool again = true;
                while (again)
                {
                    data = new byte[1024];
                    recv = client.Receive(data);
                    if (recv == 0)
                        break;

                    string line = Encoding.ASCII.GetString(data, 0, recv);
                    Console.WriteLine(line);

                    string[] tokens = line.Trim().Split(new char[] { ' ' });
                    if (tokens != null && tokens.Length > 0 && tokens[0].Trim() != "")
                    {
                        switch (tokens[0].ToLower())
                        {
                            case "start":
                                if (tokens.Length == 2 || tokens.Length == 4)
                                {
                                    TVServer.StartStream(tokens);
                                    SendText(ref client, "Stream started.");
                                }
                                else
                                {
                                    SendText(ref client, HELP);
                                }
                                break;
                            case "stop":
                                TVServer.StopStream();
                                SendText(ref client, "Stream stopped.");
                                again = false;
                                break;
                            default:
                                SendText(ref client, HELP);
                                break;
                        }
                    }
                }
                Console.WriteLine("Disconnected from {0}", clientep.Address);
                client.Close();
                newsock.Close();
            }
        }

        /// <summary>
        /// Send text back to the client.
        /// </summary>
        /// <param name="client"></param>
        /// <param name="text"></param>
        private static void SendText(ref Socket client, string text)
        {
            byte[] data = Encoding.ASCII.GetBytes(text);
            client.Send(data, data.Length, SocketFlags.None);
        }

        /// <summary>
        /// Fire up VLC on the specified channel.
        /// </summary>
        /// <param name="tokens"></param>
        public static void StartStream(string[] tokens)
        {
            //vlc dshow:// :dshow-vdev="Hauppauge WinTV PVR PCI II Capture" :dshow-adev="none" :dshow-size="" :dshow-caching=200 :dshow-chroma="" :dshow-fps=0.000000 :no-dshow-config :no-dshow-tuner :dshow-tuner-channel=31 :dshow-tuner-country=0 :dshow-tuner-input=1 :dshow-video-input=-1 :dshow-audio-input=-1 :dshow-video-output=-1 :dshow-audio-output=-1 :sout=#transcode{vcodec=WMV2,vb=3000,scale=1}:duplicate{dst=std{access=http,mux=asf,dst=:1234}}
            string channel = tokens[1];
            string videobitrate = (tokens.Length == 4) ? tokens[2] : "1024";   
            //TODO: Support audio
            //string audiobitrate = (tokens.Length == 4) ? tokens[3] : "128";

            string args = String.Format("dshow:// :dshow-vdev=\"{0}\" :dshow-adev=\"none\" :dshow-size=\"\" :dshow-caching=200 :dshow-chroma=\"\" :dshow-fps=0.000000 :no-dshow-config :no-dshow-tuner :dshow-tuner-channel={1} :dshow-tuner-country=0 :dshow-tuner-input=1 :dshow-video-input=-1 :dshow-audio-input=-1 :dshow-video-output=-1 :dshow-audio-output=-1 :sout=#transcode{{vcodec=WMV2,vb={2},scale=1}}:duplicate{{dst=std{{access=http,mux=asf,dst=:{3}}}}}",
                Properties.Settings.Default.CaptureDevice,
                channel,
                videobitrate,
                Properties.Settings.Default.StreamPort.ToString());

            streamingProc = new Process();
            streamingProc.StartInfo.FileName = Path.GetFileName(Properties.Settings.Default.VLCExe);
            streamingProc.StartInfo.WorkingDirectory = Path.GetDirectoryName(Properties.Settings.Default.VLCExe);
            streamingProc.StartInfo.Arguments = args;
            streamingProc.StartInfo.CreateNoWindow = false;
            streamingProc.Start();
        }

        /// <summary>
        /// End the VLC stream.
        /// </summary>
        public static void StopStream()
        {
            streamingProc.Kill();
            streamingProc = null;
        }
    }
}